/*
 *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: Chill 庄骞 (smallchill@163.com)
 */
package org.springblade.modules.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.extern.slf4j.Slf4j;
import org.springblade.common.constant.TemplateMsgConstant;
import org.springblade.common.utils.message.MessageCenterUtils;
import org.springblade.common.utils.message.domain.MessageParam;
import org.springblade.common.utils.message.domain.SendRequest;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.constant.BladeConstant;
import org.springblade.core.tool.utils.Func;
import org.springblade.modules.admin.entity.MaintainSignCalendar;
import org.springblade.modules.admin.entity.UserNotice;
import org.springblade.modules.admin.entity.UserWorkforceSign;
import org.springblade.modules.admin.service.IMaintainSignCalendarService;
import org.springblade.modules.admin.service.IUserNoticeService;
import org.springblade.modules.admin.service.WeChatDialogueService;
import org.springblade.modules.admin.utils.AsyncUtils;
import org.springblade.modules.admin.vo.UserReportConfigListVO;
import org.springblade.modules.admin.mapper.UserWorkforceSignMapper;
import org.springblade.modules.admin.service.IUserWorkforceSignService;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.modules.admin.vo.sign.SignWorkforceAddVo;
import org.springblade.modules.admin.vo.sign.SignWorkforceUserVo;
import org.springblade.modules.desk.entity.Notice;
import org.springblade.modules.desk.service.INoticeService;
import org.springblade.modules.system.entity.Dept;
import org.springblade.modules.system.service.IDeptService;
import org.springblade.modules.system.service.IUserService;
import org.springblade.modules.system.vo.SimpleUserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.metadata.IPage;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户考勤排班计划表 服务实现类
 *
 * @author ysq
 * @since 2024-10-09
 */
@Service
@Slf4j
public class UserWorkforceSignServiceImpl extends BaseServiceImpl<UserWorkforceSignMapper, UserWorkforceSign> implements IUserWorkforceSignService {

	/**
	 * 1:循环值班2:固定值班
	 */
	private final static Integer[] SIGN_TYPE = {0, 1, 2};

	@Autowired
	private IMaintainSignCalendarService maintainSignCalendarService;

	@Autowired
	private INoticeService noticeService;

	@Autowired
	private IUserNoticeService userNoticeService;
	@Autowired
	private IUserService userService;
	@Autowired
	private AsyncUtils asyncUtils;

	@Autowired
	private IDeptService deptService;

	@Autowired
	private MessageCenterUtils messageCenterUtils;


	@Override
	public void batchAdd(List<SignWorkforceAddVo> signWorkforceAddVos) {
		//第一步首先清理已经存在的日期
		for (SignWorkforceAddVo signWorkforceAddVo : signWorkforceAddVos) {
			List<Date> dates = signWorkforceAddVo.getDates();
			for (Date date : dates) {
				Integer yyyyMMdd = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
				//如果单个添加删除当前人的排班信息
				if (signWorkforceAddVos.get(0).getIsBatch() == 0) {
					signWorkforceAddVos.forEach(o -> o.setGroupName(deptService.getById(o.getGroupId()).getDeptName()));
					List<Long> userIds = signWorkforceAddVo.getUserIds();
					for (Long userId : userIds) {
						this.remove(Wrappers.<UserWorkforceSign>lambdaQuery().eq(UserWorkforceSign::getSignDate, yyyyMMdd).eq(UserWorkforceSign::getUserId, userId));
					}
				} else {
					//如果批量删除当天的数据
					this.remove(Wrappers.<UserWorkforceSign>lambdaQuery().eq(UserWorkforceSign::getSignDate, yyyyMMdd));
				}
			}
		}
		if (signWorkforceAddVos.get(0).getUserIds() == null) {
			return;
		}
		for (int q = 0; q < signWorkforceAddVos.size(); q++) {
			SignWorkforceAddVo signWorkforceAddVo = signWorkforceAddVos.get(q);
			List<Date> dates = signWorkforceAddVo.getDates();
			List<UserWorkforceSign> saveList = new ArrayList<>(16);
			//第二步处理循环值班
			if (signWorkforceAddVo.getSignType().equals(SIGN_TYPE[1])) {
				List<Long> userIds = signWorkforceAddVo.getUserIds();
				//第一步如果日期和人员数量相等或者小于,一个人一个日期坑位
				if (dates.size() == userIds.size() || dates.size() < userIds.size()) {
					for (int i = 0; i < dates.size(); i++) {
						Integer yyyyMMdd = Convert.toInt(DateUtil.format(dates.get(i), "yyyyMMdd"));
						UserWorkforceSign userWorkforceSign = new UserWorkforceSign();
						userWorkforceSign.setGroupId(signWorkforceAddVo.getGroupId());
						userWorkforceSign.setGroupName(signWorkforceAddVo.getGroupName());
						userWorkforceSign.setSignDate(yyyyMMdd);
						userWorkforceSign.setSignTime(dates.get(i));
						userWorkforceSign.setUserId(userIds.get(i));
						saveList.add(userWorkforceSign);
					}
				}
				//第二步如果日期大于人员数量相等，循环排列
				if (dates.size() > userIds.size()) {
					//轮询算法设置用户
					Long[] arr = userIds.toArray(new Long[userIds.size()]);
					int index = userIds.size() - 1;
					for (int i = 0; i < dates.size(); i++) {
						Integer yyyyMMdd = Convert.toInt(DateUtil.format(dates.get(i), "yyyyMMdd"));
						UserWorkforceSign userWorkforceSign = new UserWorkforceSign();
						userWorkforceSign.setGroupId(signWorkforceAddVo.getGroupId());
						userWorkforceSign.setGroupName(signWorkforceAddVo.getGroupName());
						userWorkforceSign.setSignDate(yyyyMMdd);
						int nextIndex = (index + 1) % arr.length;
						index = nextIndex;
						userWorkforceSign.setUserId(arr[index]);
						userWorkforceSign.setSignTime(dates.get(i));
						saveList.add(userWorkforceSign);
					}
				}
			}

			//第三步处理固定值班
			if (signWorkforceAddVo.getSignType().equals(SIGN_TYPE[2])) {
				for (int i = 0; i < dates.size(); i++) {
					for (int j = 0; j < signWorkforceAddVo.getUserIds().size(); j++) {
						Integer yyyyMMdd = Convert.toInt(DateUtil.format(dates.get(i), "yyyyMMdd"));
						UserWorkforceSign userWorkforceSign = new UserWorkforceSign();
						userWorkforceSign.setGroupId(signWorkforceAddVo.getGroupId());
						userWorkforceSign.setGroupName(signWorkforceAddVo.getGroupName());
						userWorkforceSign.setSort(q);
						userWorkforceSign.setSignDate(yyyyMMdd);
						userWorkforceSign.setSignTime(dates.get(i));
						userWorkforceSign.setUserId(signWorkforceAddVo.getUserIds().get(j));
						saveList.add(userWorkforceSign);
					}
				}
			}
			this.saveBatch(saveList);
		}

	}

	@Override
	public void add(SignWorkforceAddVo signWorkforceAddVo) {
		Date date = signWorkforceAddVo.getDates().get(0);
		Integer yyyyMMdd = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
		List<UserWorkforceSign> saveList = new ArrayList<>(16);
		this.remove(Wrappers.<UserWorkforceSign>lambdaQuery().eq(UserWorkforceSign::getGroupId, signWorkforceAddVo.getGroupId()).in(UserWorkforceSign::getUserId, signWorkforceAddVo.getUserIds()).eq(UserWorkforceSign::getSignDate, yyyyMMdd));
		for (int i = 0; i < signWorkforceAddVo.getUserIds().size(); i++) {
			UserWorkforceSign userWorkforceSign = new UserWorkforceSign();
			userWorkforceSign.setGroupId(signWorkforceAddVo.getGroupId());
			userWorkforceSign.setGroupName(signWorkforceAddVo.getGroupName());
			userWorkforceSign.setSignDate(yyyyMMdd);
			userWorkforceSign.setSignTime(date);
			userWorkforceSign.setUserId(signWorkforceAddVo.getUserIds().get(i));
			saveList.add(userWorkforceSign);
		}
		this.saveBatch(saveList);
	}


	@Override
	public List<SignWorkforceUserVo> groupList(Date date, Integer holiday) {
		Integer yyyyMMdd = Convert.toInt(DateUtil.format(date, "yyyyMMdd"));
		Integer yyyyMMdd2 = Convert.toInt(DateUtil.format(new Date(), "yyyyMMdd"));
		//如果是工作日并且时间大于当前时间，返回考勤人员
		if (holiday == 0 && yyyyMMdd >= yyyyMMdd2) {
			return this.getSignUserList(new SignWorkforceAddVo());
		}
		List<UserWorkforceSign> userList;
		if (holiday == 1 && yyyyMMdd < yyyyMMdd2) {
			userList = baseMapper.groupList(yyyyMMdd, 2, null, null);
		} else {
			MaintainSignCalendar maintainSignCalendar = maintainSignCalendarService.getOne(Wrappers.<MaintainSignCalendar>lambdaQuery().orderByDesc(MaintainSignCalendar::getCreateTime).last("limit 1"));
			userList = baseMapper.groupList(yyyyMMdd, holiday, null, maintainSignCalendar.getSignSetingId());
		}
		List<Long> groupIds = userList.stream().map(UserWorkforceSign::getGroupId).distinct().collect(Collectors.toList());
		Map<Long, List<UserWorkforceSign>> listMap = userList.stream().collect(Collectors.groupingBy(UserWorkforceSign::getGroupId));
		List<SignWorkforceUserVo> resultList = new ArrayList<>(16);
		for (Long groupId : groupIds) {
			SignWorkforceUserVo signWorkforceUserVo = new SignWorkforceUserVo();
			signWorkforceUserVo.setGroupId(groupId);
			List<UserWorkforceSign> userWorkforceSigns = listMap.get(groupId);
			signWorkforceUserVo.setGroupName(userWorkforceSigns.get(0).getGroupName());
			signWorkforceUserVo.setSort(userWorkforceSigns.get(0).getSort());
			signWorkforceUserVo.setUserList(userWorkforceSigns);
			resultList.add(signWorkforceUserVo);
		}
		return resultList;
	}

	@Override
	public List<List<String>> listExcel(Date beginDate, Date endDate) {
		Map<Integer, String> map = new HashMap<>(7);
		map.put(1, "一");
		map.put(2, "二");
		map.put(3, "三");
		map.put(4, "四");
		map.put(5, "五");
		map.put(6, "六");
		map.put(7, "日");
		//获取节假日
		Integer holiday = 1;
		List<MaintainSignCalendar> signCalendarList = maintainSignCalendarService.list(Wrappers.<MaintainSignCalendar>lambdaQuery().between(MaintainSignCalendar::getDate, beginDate, endDate));
		List<UserWorkforceSign> userWorkforceSigns = baseMapper.groupList(Convert.toInt(DateUtil.format(beginDate, "yyyyMMdd")), holiday, Convert.toInt(DateUtil.format(endDate, "yyyyMMdd")), signCalendarList.get(0).getSignSetingId());
		List<Long> groupIds = userWorkforceSigns.stream().map(UserWorkforceSign::getGroupId).distinct().collect(Collectors.toList());
		Map<Long, List<UserWorkforceSign>> listMap = userWorkforceSigns.stream().collect(Collectors.groupingBy(UserWorkforceSign::getGroupId));

		signCalendarList.forEach(o -> o.setSignDate(Convert.toInt(DateUtil.format(o.getDate(), "yyyyMMdd"))));

		List<MaintainSignCalendar> signCalendars = signCalendarList.stream().filter(o -> o.getHoliday() == 1).collect(Collectors.toList());
		if (signCalendars.size() == 0) {
			return new ArrayList<>();
		}
		List<List<String>> rows = new ArrayList<>();
		List<String> title = new ArrayList<>(3);
		List<String> title2 = new ArrayList<>(3);
		title2.add(Convert.toInt(DateUtil.format(beginDate, "MM")) + "月假期值班信息:" + Convert.toStr(DateUtil.format(beginDate, "MM月dd日")) + "-" + Convert.toStr(DateUtil.format(endDate, "MM月dd日")));
		rows.add(title2);
		title.add("值班组");
		for (MaintainSignCalendar maintainSignCalendar : signCalendars) {
			title.add(Convert.toStr(DateUtil.format(maintainSignCalendar.getDate(), "MM-dd")) + " " + map.get(maintainSignCalendar.getWeek()));
		}
		rows.add(title);
		for (Long groupId : groupIds) {
			List<UserWorkforceSign> workforceSigns = listMap.get(groupId).stream().distinct().collect(Collectors.toList());
			List<String> row = new ArrayList<>(3);
			row.add(workforceSigns.get(0).getGroupName());
			Map<Integer, List<UserWorkforceSign>> listMap1 = workforceSigns.stream().collect(Collectors.groupingBy(UserWorkforceSign::getSignDate));
			for (MaintainSignCalendar maintainSignCalendar : signCalendars) {
				List<UserWorkforceSign> signList = listMap1.get(maintainSignCalendar.getSignDate());
				if (signList == null || signList.size() == 0) {
					row.add("休");
				} else {
					String str = "";
					signList = signList.stream().distinct().collect(Collectors.toList());
					for (UserWorkforceSign userWorkforceSign : signList) {
						str = str + "," + userWorkforceSign.getUserName() + "-" + userWorkforceSign.getPhone();
					}
					row.add(str.substring(str.indexOf(",") + 1));
				}
			}
			rows.add(row);
		}
		return rows;
	}

	@Override
	public List<MaintainSignCalendar> signDateList(SignWorkforceAddVo signWorkforceAddVo) {
		List<MaintainSignCalendar> list = baseMapper.signDateList(signWorkforceAddVo);
		return list;
	}

	@Override
	public void sendMessage(SignWorkforceAddVo signWorkforceAddVo) {
		List<MaintainSignCalendar> maintainSignCalendars = signDateList(signWorkforceAddVo);
		List<MaintainSignCalendar> collect = maintainSignCalendars.stream().filter(o -> o.getHoliday() == 1 && o.getIsUsers() == null && Convert.toInt(DateUtil.format(o.getDate(), "yyyyMMdd")) > Convert.toInt(DateUtil.format(new Date(), "yyyyMMdd"))).collect(Collectors.toList());
		if (collect.size() > 0) {
			throw new ServiceException("请完成当月所有节假日值班设置再发通知");
		}
		List<UserWorkforceSign> signs = baseMapper.list(Convert.toInt(DateUtil.format(signWorkforceAddVo.getBeginDate(), "yyyyMMdd")), Convert.toInt(DateUtil.format(signWorkforceAddVo.getEndDate(), "yyyyMMdd")), maintainSignCalendars.get(0).getSignSetingId());
		if (signs.size() == 0) {
			throw new ServiceException("请完成当月所有节假日值班设置再发通知");
		}
		Notice notice = new Notice();
		notice.setTitle(Convert.toStr(DateUtil.format(signWorkforceAddVo.getEndDate(), "yyyy-MM")) + "月值班通知");
		notice.setContent("请查收" + Convert.toStr(DateUtil.format(signWorkforceAddVo.getBeginDate(), "MM月dd日")) + "-" + Convert.toStr(DateUtil.format(signWorkforceAddVo.getEndDate(), "MM月dd日")) + "值班安排");
		notice.setCategory(10);
		notice.setSignDate(signWorkforceAddVo.getEndDate());
		notice.setReleaseTime(new Date());
		boolean isSave = noticeService.save(notice);
		List<Long> userIds = signs.stream().map(UserWorkforceSign::getUserId).distinct().collect(Collectors.toList());
		if (isSave) {
			List<UserNotice> userNotices = new ArrayList<>();
			for (Long userId : userIds) {
				UserNotice userNotice = new UserNotice();
				userNotice.setUserId(userId);
				userNotice.setNoticeId(notice.getId());
				userNotices.add(userNotice);
			}
			userNoticeService.saveBatch(userNotices);
		}
		if (isSave) {
			// 2025-09 新增极光App消息
			log.info("============= 发送排班微信、极光通知 =============");
			for (Long userId : userIds) {
				SimpleUserVO assignUserInfo = userService.getSimpleUserInfo(userId);
				if (Func.isNotEmpty(assignUserInfo)) {
					String skipUrl = "/pageModules/signPage/siginIndex?month=" + Convert.toStr(DateUtil.format(signWorkforceAddVo.getEndDate(), "MM")) + "&duty=true&year=" + Convert.toStr(DateUtil.format(signWorkforceAddVo.getEndDate(), "yyyy"));
					String monthDesc = Convert.toStr(DateUtil.format(signWorkforceAddVo.getBeginDate(), "yyyy-MM") + "月值班安排");
					if (Func.isNotEmpty(assignUserInfo.getOpenid())) {
						Map<String, String> templateDataMap = new HashMap<>(8);
						templateDataMap.put("thing4", monthDesc);
						templateDataMap.put("thing3", assignUserInfo.getUserName());
						templateDataMap.put("params", skipUrl);
						log.info("发送排班微信通知，用户：{}, 通知：{}", assignUserInfo.getOpenid(), templateDataMap);
						asyncUtils.sendWeiXinTemplate(assignUserInfo.getOpenid(), TemplateMsgConstant.WORK_FORCE_MESSASGE, templateDataMap);
					}
					if (Func.isNotEmpty(assignUserInfo.getRegistrationId())){
						Map<String, String> contentMap = new HashMap<>(4);
						contentMap.put("title", "值班通知");
						contentMap.put("content", "您收到" + monthDesc + ",点击查看详情！");
						contentMap.put("url", skipUrl);
						contentMap.put(BladeConstant.MDC_TENANT_ID_KEY, AuthUtil.getTenantId());
						SendRequest request = SendRequest.builder()
							.messageTemplateId("1902915055486881794")
							.messageParam(
								MessageParam.builder()
									.receiver(String.valueOf(assignUserInfo.getRegistrationId()))
									.variables(contentMap)
									.build()
							).build();
						R r = messageCenterUtils.sendMessage(request);
						log.info("APP推送结果:" + JSON.toJSONString(r));
					}
				}
			}
		}
	}

	@Override
	public List<UserReportConfigListVO> listDetail(IPage<UserReportConfigListVO> page, SignWorkforceAddVo signWorkforceAddVo) {
		//表示节假日
		Integer holiday = 2;
		//第一步获取考勤日历
		List<MaintainSignCalendar> signCalendarList = maintainSignCalendarService.list(Wrappers.<MaintainSignCalendar>lambdaQuery().between(MaintainSignCalendar::getDate, signWorkforceAddVo.getBeginDate(), signWorkforceAddVo.getEndDate()));
		signCalendarList.forEach(o -> o.setSignDate(Convert.toInt(DateUtil.format(o.getDate(), "yyyyMMdd"))));
		//第二步查询考勤人员
		signWorkforceAddVo.setSignSetingId(signCalendarList.get(0).getSignSetingId());
		List<UserReportConfigListVO> list = baseMapper.signUserList(page, signWorkforceAddVo);

		//第三步获取值班人员
		List<UserWorkforceSign> signs = baseMapper.list(Convert.toInt(DateUtil.format(signWorkforceAddVo.getBeginDate(), "yyyyMMdd")), Convert.toInt(DateUtil.format(signWorkforceAddVo.getEndDate(), "yyyyMMdd")), signCalendarList.get(0).getSignSetingId());
		Map<Long, List<UserWorkforceSign>> listMap = signs.stream().collect(Collectors.groupingBy(UserWorkforceSign::getUserId));

		//第四步组装数据
		List<UserReportConfigListVO> resultList = new ArrayList<>(20);
		for (UserReportConfigListVO userReportConfigListVO : list) {
			List<UserWorkforceSign> userWorkforceSigns = listMap.get(userReportConfigListVO.getUserId());
			List<MaintainSignCalendar> userSignDateList = JSON.parseArray(JSON.toJSONString(signCalendarList), MaintainSignCalendar.class);
			if (userWorkforceSigns == null || userWorkforceSigns.size() == 0) {
				userReportConfigListVO.setList(userSignDateList);
				resultList.add(userReportConfigListVO);
				continue;
			}
			//如果包含表示有值班
			List<Integer> collect = userWorkforceSigns.stream().map(UserWorkforceSign::getSignDate).distinct().collect(Collectors.toList());
			for (MaintainSignCalendar maintainSignCalendar : userSignDateList) {
				//为节假日的时候判断是否有值班数据
				if (maintainSignCalendar.getHoliday() == 1) {
					if (collect.contains(maintainSignCalendar.getSignDate())) {
						maintainSignCalendar.setHoliday(holiday);
					}
				}
			}
			userReportConfigListVO.setList(userSignDateList);
			resultList.add(userReportConfigListVO);
		}
		return resultList;
	}

	@Override
	public List<SignWorkforceUserVo> getSignUserList(SignWorkforceAddVo signWorkforceAddVo) {
		MaintainSignCalendar maintainSignCalendar = maintainSignCalendarService.getOne(Wrappers.<MaintainSignCalendar>lambdaQuery().orderByDesc(MaintainSignCalendar::getCreateTime).last("limit 1"));
		signWorkforceAddVo.setSignSetingId(maintainSignCalendar.getSignSetingId());
		List<UserWorkforceSign> list = baseMapper.getSignUserList(signWorkforceAddVo);
		Map<Long, List<UserWorkforceSign>> listMap = list.stream().collect(Collectors.groupingBy(UserWorkforceSign::getGroupId));
		List<Long> groupIds = list.stream().map(UserWorkforceSign::getGroupId).distinct().collect(Collectors.toList());
		List<SignWorkforceUserVo> resultList = new ArrayList<>();
		for (Long groupId : groupIds) {
			SignWorkforceUserVo signWorkforceUserVo = new SignWorkforceUserVo();
			List<UserWorkforceSign> userWorkforceSigns = listMap.get(groupId);
			signWorkforceUserVo.setGroupId(groupId);
			signWorkforceUserVo.setGroupName(userWorkforceSigns.get(0).getGroupName());
			signWorkforceUserVo.setUserList(userWorkforceSigns);
			resultList.add(signWorkforceUserVo);
		}
		//处理小组下无人的情况
		if (signWorkforceAddVo.getGroupIds() != null) {
			List<Long> groupIds1 = signWorkforceAddVo.getGroupIds();
			groupIds1.removeAll(groupIds);
			if (groupIds1.size() > 0) {
				List<Dept> depts = deptService.listByIds(groupIds1);
				for (Dept dept : depts) {
					SignWorkforceUserVo signWorkforceUserVo = new SignWorkforceUserVo();
					signWorkforceUserVo.setGroupId(dept.getId());
					signWorkforceUserVo.setGroupName(dept.getDeptName());
					signWorkforceUserVo.setUserList(new ArrayList<>());
					resultList.add(signWorkforceUserVo);
				}
			}
		}
		return resultList;
	}
}
